------------Grammar Gremlins-----------
A 4am crack                  2015-11-06
-------------------. updated 2019-05-10
                   |___________________

Name: Grammar Gremlins
Version: 091286 according to disk label
Genre: educational
Year: 1987
Authors: Santa Barbara Softworks
Publisher: Davidson and Associates
Media: double-sided 5.25-inch floppy
OS: Pascal
Previous cracks: none
Similar cracks:
  #291 Grammar Gremlins 1986
  #031 Addition and Subtraction 1

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  read error on last pass of side A
  side B copies with no errors

All further discussion will focus on
side A.

Locksmith Fast Disk Backup
  can't read T22,S00, copy hangs during
  boot

EDD 4 bit copy (no sync, no count)
  works

Copy ][+ nibble editor
  T22,S00 does not appear to use
  modified address or epilogue bytes.
  It's just not there at all.

Disk Fixer
  No way to read T22,S00

Why didn't COPYA work?
  intentionally bad sector on track $22

Why didn't Locksmith FDB work?
  probably a nibble check looking for
  that "bad" sector on track $22

The program appears to be written in
Apple Pascal. The boot process starts
with several sequential disk reads,
then clears the screen and displays a
block cursor in the upper-left corner.
(I also recognize the hi-res graphics
font and the way the program draws it.
I'll take "Skills I Never Thought I'd
Use" for $200, Alex, but here we are.)

Next steps:

  1. Use the tools on an Apple Pascal
     system disk to verify that this
     disk really uses Apple Pascal
  2. Find the HELLO program (usually
     SYSTEM.STARTUP) and decompile it
  3. Hack the p-code to disable the
     copy protection

                   ~

               Chapter 1
          P-Code Is Best Code


The first thing I'll need is an Apple
Pascal work disk, instead of my usual
(DOS 3.3-compatible) Diversi-DOS disk.
The second thing is a p-code decompiler
from
<http://apple2.callapple.org/software/
tribby/apascal.html>

[S6,D1=my work disk]
[S6,D2=original disk]

]PR#6
...

Command: F(ile, E(dit, R(un, C(omp,
L(ink, X(ecute, A(ssem, ?  [1.3]

--> "F" [runs the Apple Pascal file
management utility]

Filer: L(dir E(dir R(em T(rans C(hng
D(ate P(refix K(rnch Z(ero V(ols Q(uit?

--> "V" [shows which disks are online]

Volume # - Volume Name - # Blocks

    1        CONSOLE:
    2        SYSTERM:
    4        GGWORK:        280
    5        GREMLIN:       280
   11        <no dir>       280

System volume is - GGWORK:
Prefix volume is - GGWORK:

Confirmed: the original disk is
written in Apple Pascal, and it's
readable by the standard Apple Pascal
filer. Apple Pascal volumes can be
referenced by name, like ProDOS
volumes. This disk's name is GREMLIN:.

--> "L" [lists a volume]

Directory listing of what volume ?

--> "GREMLIN:" [name of original disk]

GREMLIN:
SYSTEM.PASCAL       28    3-Sep-85
SYSTEM.CHARSET       2   14-Jun-79
SYSTEM.MISCINFO      1    4-Mar-86
ANIMALS.DATA        17    4-Mar-86
OLDTITLE.PIC        17   28-Mar-86
TITLE1.PIC          17   27-Mar-86
SYSTEM.LIBRARY      15    2-May-86
EDITOR.HELP          2    5-Apr-86
FULLHOUSE.PIC       17    8-Apr-86
SYSTEM.APPLE        32    3-Sep-85
GG.CODE             95   16-Sep-87
TITLE2.PIC          17    4-Nov-87
SYSTEM.STARTUP       2    3-Nov-87
PROTECT3.CODE        2    3-Nov-87
14/14 files <listed/in dir>, 270 blocks
used, 10 unused, 10 in largest

Under Apple Pascal, "SYSTEM.STARTUP"
is the program that runs automatically
on boot, like HELLO under DOS 3.3. I'm
hoping that's where the copy protection
routine lives. (If not, that "PROTECT3"
file looks suspicious/promising.)

--> "Q" [return to main menu]


Command: F(ile, E(dit, R(un, C(omp,
L(ink, X(ecute, A(ssem, ?  [1.3]

--> "X" [execute a program]


Execute what file (<ret> to exit) ?

--> "DISASSM" [this is the program I
downloaded from callapple.org]


APPLE PASCAL/6502 DIS-ASSEMBLER
VERSION: July 27, 1982

Name of OUTPUT file (default is
CONSOLE:):

--> "GG.TEXT"


Name of code file:

--> "GREMLIN:SYSTEM.STARTUP"


Decode PROTECT  machine code?

Hmm, that's interesting. Not sure what
it means, but yes, let's do that.

--> "Y"

...a surprisingly short time later...

Success! Here is the complete output
file, GG.TEXT:

                 --v--

Code file = GREMLIN:SYSTEM.STARTUP
The following library bytes are non-
zero:

PROTECT  is a linked segment  (P-code
vers.5), length = 70 bytes
These SYSTEM.LIBRARY intrinsics are
used:
 28 (Chainstuff)
** # PROCS = 1, SEGMENT # = 1
Disassembly for procedure # 1; Lex
level = 0
P-code procedure 1
Code = 51, parameters = 4, data = 512
bytes; Jump table = 2 words
   0: B9 2A               UJP    42
   2: CD 1C 01            CXP    28,1
   5: 04                  SLDC 4
   6: A5 03               LAO    3
   8: 00                  SLDC 0
   9: C7 00 02            LDCI   512
  12: C7 10 01            LDCI   272
  15: 00                  SLDC 0
  16: 9E 05               CSP    5
  18: 9E 22               CSP    34
  20: 00                  SLDC 0
  21: CB                  NEQI
  22: A1 0A               FJP    10
  24: D7                  NOP
  25: A6 02 47 47         LSA    2,"GG"
  29: CD 1C 02            CXP    28,2
  32: B9 05               UJP    5
  34: 01                  SLDC 1
  35: A1 02               FJP    2
  37: B9 F6               UJP    -10
  39: 1C                  SLDC 28
  40: 9E 16               CSP    22
  42: B9 05               UJP    5
  44: 1C                  SLDC 28
  45: 9E 15               CSP    21
  47: B9 F4               UJP    -12
  49: C1 00               RBP    0
Jump table:
  52: 32 00   .WORD   R2 (Entry # -12)
  54: 14 00   .WORD   R34 (Entry # -10)

                 --^--

Let's go through this line by line.

                   ~

               Chapter 1
          P-Code Is Best Code


The numbers along the left of the
disassembly are byte offsets in
decimal, not hex. It looks like the
numbers along the right are also in
decimal, but the opcodes are in hex.

   0: B9 2A               UJP    42

According to Appendix A in the "Apple
Pascal Operating System Reference
Manual" (hereafter known as "the
f---ing manual"), "UJP" stands for
"unconditional jump." With a positive
parameter, it functions like a relative
forward jump, similar to branch opcodes
in 6502 assembly. The offset is added
to the PC of the next instruction, not
the current one. (Assembly language
branches also work this way.) In other
words, this two-byte instruction at
offset 0 branches forward to offset
0 + 2 + 42 = 44.

  44: 1C                  SLDC 28
  45: 9E 15               CSP    21

According to the f---ing manual, "SLDC
28" pushes the decimal value 28 ($1C)
to the stack. "CSP" stands for "call
standard procedure." Apple Pascal has a
number of built-in functions like "cos"
(calculate a cosine) or "readln" (read
a line of input). But what is standard
procedure #21? The f---ing manual does
not say. But I found a reference to it
in "P-Source: A Guide to the Apple
Pascal System" by Randall Hyde. On page
311 is the entire CSP table, and the
21st entry is "LDS" ("Load Segment").

Quoting Hyde: "[LDS] pops the segment
number off of the stack, gets it into
the 6502 accumulator, and then calls
the load segment subroutine. Upon
return from load segment, control is
returned to the main interpreter loop."

But what segment are we loading? #28,
which was just pushed to the stack in
the previous instruction. But what is
segment #28? According to Appendix A in
the f---ing manual, segment 0 and
segments 2-6 are reserved for the
operating system. Segment 1 is the main
program that is currently executing
(SYSTEM.STARTUP in this case). Segments
7-21 are for segments and units defined
within the program. And segments 22-31
are predefined intrinsic units.

Apple Pascal includes several libraries
like TURTLEGRAPHICS for Logo-like
graphic commands and APPLESTUFF for
access to joystick buttons and other
hardware. But which one is #28? I
finally found the answer in a post by
"Tommy" on comp.sys.apple2 in a thread
called "Wizardry re-engineering." Using
a tool called LIBMAP, Tommy dug into
the SYSTEM.LIBRARY file (part of the
Apple Pascal operating system) and
found the internal segment numbers for
each intrinsic unit in Apple Pascal:

20 TURTLEGRAPHICS (Logo-like graphics)
22 APPLESTUFF (hardware access)
28 CHAINSTUFF (launching programs)
29 TRANSCEND (geometry functions)
30 LONGINTIO (long integer support)
31 PASCALIO (parsing decimals)

Thus, the p-code instruction "SLDC 28",
followed by "CSP 21", will load the
CHAINSTUFF intrinsic unit from
SYSTEM.LIBRARY.

  47: B9 F4               UJP    -12

This is another unconditional jump, but
according to the f---ing manual,
negative parameters are not simply
backward branches. Instead, they are
referenced by their position in the
"jump table" at the end of the
procedure. This is the jump table:

Jump table:
  52: 32 00   .WORD   R2 (Entry # -12)
  54: 14 00   .WORD   R34 (Entry # -10)

So the instruction "UJP -12" jumps to
the address listed as "Entry # -12",
which is R2. "2" refers to the
instruction at byte offset 2, which is
back near the beginning:

   2: CD 1C 01            CXP    28,1

"CXP" stands for "Call eXternal
Procedure", and the f---ing manual says
it takes two arguments, a segment #
and a procedure # (in that order).

According to Tommy, procedure #28 in
segment #1 is "uses chainstuff". That
makes sense, I suppose, since we just
got finished loading that library into
memory. Now we're signaling that we
want to use it.

That's it. Four hours of research, and
"uses chainstuff" is as far as I've
gotten. Did I mention I have no idea
what I'm doing?  This is fun. Are you
having fun? I'm having fun.

                   ~

               Chapter 2
 Standard Procedure Is Best Procedure


Continuing at offset 5:

   5: 04                  SLDC 4
   6: A5 03               LAO    3
   8: 00                  SLDC 0
   9: C7 00 02            LDCI   512
  12: C7 10 01            LDCI   272
  15: 00                  SLDC 0
  16: 9E 05               CSP    5

According to the f---ing manual, "SLDC
4" just pushes the value 4 to the
stack. (To conserve space, p-code has
lots of one-byte instructions that all
mean "push me to the stack.")

"LAO" is "load global address". The
f---ing manual has this to say about
that: "LAO B. Fetch the word with
offset 'B' in BASE activation record
and push it." Then more pushing, then
"CSP 5". According to Hyde, standard
procedure #5 is "UREAD" (unit read).

According to Tommy, the unit read
procedure is a low-level disk read,
like calling the RWTS directly under
DOS 3.3. It takes 5 parameters. The
first parameter is a volume number. In
Apple Pascal, the volume number of the
disk you're booting from is always 4.
(I saw this earlier when I listed all
online volumes from the Filer.) That
explains the "SLDC 4" and "LAO 3"
instructions. It's getting a reference
to volume #4 and pushing it to the
stack, in preparation for the unit read
procedure to pull it off the stack and
use the value as its first parameter.

The second and third parameters to the
UREAD function are BUFFER (0) and
BUFFERSIZE (512). So we're storing the
result of the read in a 512-byte
buffer, which makes sense. Like ProDOS,
Pascal organizes disks into "blocks" of
two sectors each. There are 280 blocks
on a 5.25-inch floppy.

The 4th parameter is the block number
to read. Since a block is two sectors,
there are 8 blocks per track. Counting
from 0, block 272 would be the first
block of track $22. According to the
"Comparison of Sector Skewing" figure
in "Beneath Apple DOS" (p. 3-23), this
block includes track $22, sector $00,
which is was the only unreadable sector
on the original disk.

We are definitely on the right track.

The "CSP 5" actually calls the UREAD
function.

Continuing at offset 18:

  18: 9E 22               CSP    34

According to Hyde, standard procedure
#34 is "IOR". It pushes the value of
the IORESULT global (set by UREAD) to
the stack. In other words, we're
checking whether the read worked. But
remember! On the original disk, this
block was unreadable, because T22,S00
was intentionally bad. There's no
special logic here to change the RWTS
to make it readable. The original disk
is just reading the block like any
other and checking to ensure that the
read failed.

  20: 00                  SLDC 0
  21: CB                  NEQI

"SLDC" pushes 0 to the stack.

"NQI" is an "is-not-equals" test. It
compares the top value on the stack
with the next-to-top value on the
stack. The top value is 0, since the
instruction just before it (at offset
28) was "SLDC 0". The next-to-top value
is the return value from calling
standard procedure #34. So I think this
is saying "if UREAD did not return 0,
then..."

Then what?

  22: A1 0A               FJP    10

"FJP" stands for "false jump," or less
confusingly, "jump if false." If the
previous test evaluated to false, then
jump. The previous test was "did UREAD
not return 0?" So I had it backwards in
the previous paragraph. The proper
prose description of this code is "if
UREAD *did* return 0, then jump." Jump
to where? Forward 10 bytes, to offset
22 + 2 + 10 = 34.

Without decompiling every single
instruction between 24 and 34, it seems
pretty clear that this is the branch
that my copy is taking and the original
disk is not. The program expects block
272 to be unreadable, but on my copy,
block 272 reads just fine. (This also
explains why my EDD bit copy worked.)

If I change that "NEQI" (is-not-equals)
instruction at offset 21 to an "EQUI"
(is-equals) instruction, that would
reverse the logic entirely. Instead of
saying, "if UREAD returned no error,
branch to the failure path," it would
say, "if UREAD returned an error,
branch to the failure path."

I like this solution for two reasons:
first, it's the simplest thing that
could possibly work. It's only a one-
byte change, so it doesn't require
inserting or removing any bytes of the
p-code (which is convoluted and
intimidating, to say the least). And
second, I enjoy the irony of turning a
copy protection check into a copy
deprotection check.

Turning to my trusty Disk Fixer sector
editor, I searched the disk for the
first few opcodes ("B9 2A CD 1C 01",
from the very beginning of the
procedure) and found this code twice on
track $21. The f---ing manual tells me
that the opcode for "NEQI" is $C3.

T21,S05,$15 change "CB" to "C3"
T21,S09,$15 change "CB" to "C3"

]PR#6
...works...

Quod erat liberandum.

                   ~

              References


Here are some references on Apple
Pascal hacking that I found helpful:

Dave Tribby's Apple II Pages: Apple
Pascal
<http://apple2.callapple.org/software/
tribby/apascal.html>

The "Apple Pascal Operating System
Reference Manual" (a.k.a. "the f---ing
manual")
<http://mirrors.apple2.org.za/Apple%20I
I%20Documentation%20Project/Software/Op
erating%20Systems/Apple%20Pascal/Manual
s/Apple%20Pascal%20Operating%20System%2
0Reference%20Manual.pdf>

Appendices A and B of that manual are
available in plain text at
<http://www.siconic.com/crap/pascal>

"P-Source: A Guide to the Apple Pascal
System" by Randall Hyde
<http://pascal.hansotten.com/index.php?
page=books>

Starting in 2012, "Tommy" has written a
series of posts on comp.sys.apple2
about reverse engineering Wizardry,
which was written in Apple Pascal.

"Re-engineered: Wizardry III, Legacy of
Llylgamyn"
<https://groups.google.com/d/msg/comp.s
ys.apple2/2oDJTbQaJWU/Vge7HkIcYYwJ>

"Wizardry re-engineering"
<https://groups.google.com/d/msg/comp.s
ys.apple2/aI5ob1mLUwY/xNXh2wro6KQJ>

"Wizardry IV bootstrap bug in SYSTEM.
INTERP"
<https://groups.google.com/d/msg/comp.s
ys.apple2/YbIFL4T4R4U/3d_NcMi16EoJ>

                   ~

               Changelog


2019-05-10

- updated disk image from new source to
  fix some minor data corruption on
  side A, track $1C

2015-11-06

- initial release

---------------------------------------
A 4am crack                     No. 490
------------------EOF------------------
